package com.apache.portal.contorller;

import com.apache.api.vo.ResultMsg;
import com.apache.portal.common.util.FileOperate;
import com.apache.portal.common.util.PortalPubFactory;
import com.apache.portal.common.vo.ProgressSingleton;
import com.apache.tools.NumberUtils;
import com.apache.tools.StrUtil;
import com.apache.uct.common.ToolsUtil;
import net.sf.json.JSONObject;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * add 20170808 新增附件上传，upload
 * 必须是登录状态下
 * 调用地址： ctx+"/uploadServlet?f="+new Date().getTime();
 *
 * @author admin
 */
@SuppressWarnings("serial")
public class UploadServlet extends BaseHttpServlet {

    protected Logger log = LoggerFactory.getLogger(getClass());

    private static final long minLen = 1024 * 1024 * 5; // 5M

    private static final long maxLen = 1024 * 1024 * 1024; // 1G

    /**
     * 附件上传
     */
    @Override
    protected void doInvoke(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST,GET");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        Map<String, String> resultMap = new HashMap<String, String>();
        resultMap.put("flag", "F");

        log.info("\n");
        log.info("File upload start...");
        long starTime = new Date().getTime();

        Map<String,String> fileConfigMap =  getLocalConfigMap();
        if(fileConfigMap.size()==0){
            resultMap.put("msg", "服务端缺少相关配置");
            showJson(resultMap, request, response);
            return;
        }
        String FILE_MAX_LENGTH = StrUtil.doNull(fileConfigMap.get("fileMaxLength"),String.valueOf(minLen));//单位B
        String File_ALLOW_TYPE = fileConfigMap.get("fileAllowType");
        long fimaxLength = Long.valueOf(FILE_MAX_LENGTH).longValue();
        log.info("Config FILE_MAX_LENGTH->  " + FILE_MAX_LENGTH + "=" + getFileSize(fimaxLength));
        log.info("Config ALLOW_TYPE->  " + File_ALLOW_TYPE);
        //log.info("Data source address->  " + request.getLocalAddr() + ":" + request.getLocalPort());
        log.info("Data source url->  " + request.getRequestURI() + " 参数解析中...");
        // 配置上传参数
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
        factory.setSizeThreshold(1024 * 1024 * 10);

        // 设置临时存储目录
        factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 设置最大文件上传值
        upload.setFileSizeMax(maxLen);
        // 中文处理
        upload.setHeaderEncoding("UTF-8");
        String uploadRoot = fileConfigMap.get("fileUploadDirectory") + "/atta/"; //获取文件需要上传到的根目录
        String downloadRoot = fileConfigMap.get("fileDownloadUrl") + "/atta/";//获取文件需要下载的根目录
        String nowDataStr = new SimpleDateFormat("yyyyMMdd").format(new Date());// + "/";
        Map<String, String> paramMap = new HashMap<String, String>();
        try {
            FileItem fileItem = null;
            List<FileItem> formItems = (List<FileItem>) upload.parseRequest(request);
            if (formItems != null && formItems.size() > 0) {
                // 迭代表单数据
                //				Map<String,FileItem> uploadMap = new HashMap<String, FileItem>();
                for (int i = 0; i < formItems.size(); i++) {
                    FileItem item = formItems.get(i);
                    if (!item.isFormField()) {
                        //String fileName=item.getName();
                        fileItem = item;//拼装需要上传的文件
                    } else {
                        //保存form表单提交的其他参数
                        String name = item.getFieldName();
                        String value = item.getString();
                        request.setAttribute(name, value);
                        paramMap.put(name, value);
                    }
                }
                //				System.out.println(paramMap);

                String fileTypes = StrUtil.doNull(paramMap.get("upload_fileTypes"), File_ALLOW_TYPE);
                log.info("File request upload_fileTypes->  "+fileTypes);
                String sysEname = StrUtil.doNull(paramMap.get("upload_sysName"), "default");
                String uploadFolder = StrUtil.doNull(paramMap.get("upload_folder"),"");//自定义业务类型文件夹
                String confData = StrUtil.doNull(paramMap.get("confData"),"");//其他配置参数json格式的字符串
                log.info("File request sysEname->  "+sysEname);
                log.info("File request confData->  "+confData);
                String uploadPath = uploadRoot + sysEname + "/";// 上传目录
                if(!StrUtil.isNull(uploadFolder)&&!"undefined".equalsIgnoreCase(uploadFolder)){
                    uploadPath += uploadFolder + "/";
                }
                uploadPath += nowDataStr;// 上传目录
                String tempSessionId = StrUtil.doNull(paramMap.get("tempSessionId"), request.getSession().getId());
                String idName = paramMap.get("idname");
                String fileNum = paramMap.get("fileNum");
                resultMap.put("id", idName);
                resultMap.put("fileNum", fileNum);

                if (fileItem != null) {
                    FileOutputStream out = null;
                    try {
                        //旧文件名
                        String fileName = fileItem.getName();
                        long fileSize = fileItem.getSize();
                        String fileSizeStr = getFileSize(fileSize);
                        log.info("File name->  " + fileName+" | size[" + fileSizeStr + "] | tempSessionId[" + tempSessionId+"]");
                        if (fileSize > fimaxLength) {
                            resultMap.put("msg", "服务端暂不支持文件大小超过" + getFileSize(fimaxLength));
                            log.error("File size->  服务端暂不支持文件大小超过");
                            showJson(resultMap, request, response);
                            return;
                        }
                        String nameEx = FileOperate.getInstance().getExtension(fileName);//取得文件扩展名
//                        log.info("File extension->  " + nameEx);
                        boolean isCheckOk = checkFileType(fileItem,fileTypes);
                        if (!isCheckOk){
                            log.error("File contentType->  服务端暂不支持此格式文件上传");
                            resultMap.put("msg", "服务端暂不支持此格式文件上传");
                            showJson(resultMap, request, response);
                            return;
                        }
//                        if (fileTypes.indexOf(nameEx) < 0) {
//                            resultMap.put("msg", "文件格式不正确");
//                            showJson(resultMap, request, response);
//                            return;
//                        }
                        log.info("File directory->  " + uploadPath);
                        //判断目录是否存在
                        if (!FileOperate.getInstance().checkFolder(uploadPath)) {
                            FileOperate.getInstance().newCreateFolder(uploadPath);
                        }
                        //新文件名称
                        String wrFileName = String.valueOf(System.currentTimeMillis()) + "." + nameEx;
                        log.info("File new name->  " + wrFileName);
                        //新文件上传全路径
                        String fileNewPath = uploadPath + "/" + wrFileName;
                        log.info("File is uploading,complete path->  " + fileNewPath);

                        //使用sessionid + 文件名生成文件号
                        //向单例哈希表写入文件长度和初始进度
                        ProgressSingleton.put(tempSessionId + "_size", fileItem.getSize());
                        //文件进度长度
                        long progress = 0;
                        //用流的方式读取文件，以便可以实时的获取进度
                        InputStream in = fileItem.getInputStream();
                        String fileContentType = fileItem.getContentType();
//                        if("text/xml".equals(fileContentType)||fileSize<(1024*1024)){
                        if("text/xml".equals(fileContentType)&&fileSize<(1024*1024)){
                            log.info("文件格式是[xml]且小于1M，则不以文件流方式上传！");
                            FileOperate.getInstance().newCreateFile(fileNewPath, IOUtils.toString(in,"UTF-8"));
                        }else{
                            File file = new File(fileNewPath);
                            file.createNewFile();
                            out = new FileOutputStream(file);
                            byte[] buffer = new byte[2048];
                            int readNumber = 0;
                            while((readNumber = in.read(buffer)) != -1){//每读取一次，更新一次进度大小
                                progress = progress + readNumber;
                                //向单例哈希表写入进度
                                ProgressSingleton.put(tempSessionId + "_progress", progress);
//                            log.info("File read progress -> "+ getFileSize(progress));
                                out.write(buffer);
                            }
                        }
                        //下载全路径
                        String downloadPath = downloadRoot + sysEname +File.separator;
                        if(!StrUtil.isNull(uploadFolder)){
                            downloadPath += uploadFolder + File.separator;
                        }
                        downloadPath += nowDataStr + File.separator + wrFileName;
                        downloadPath = downloadPath.replaceAll("\\\\", "/");
                        log.info("File download full path->  " + downloadPath);
//                        // 保存文件到硬盘
//                        File storeFile = new File(fileNewPath);
//                        fileItem.write(storeFile);

                        //当文件上传完成之后，从单例中移除此次上传的状态信息
                        ProgressSingleton.remove(tempSessionId + "_size");
                        ProgressSingleton.remove(tempSessionId + "_progress");
                        in.close();

                        resultMap.put("flag", "T");
                        resultMap.put("msg", "操作成功");
                        resultMap.put("murl", downloadPath);
                        resultMap.put("fileNameOld", fileName);
                        resultMap.put("fileNameNew", wrFileName);
                        resultMap.put("filesize",fileSizeStr);
                        resultMap.put("fileType",nameEx);
                        resultMap.put("sysEname",sysEname);

                        long timeLong=(new Date().getTime() - starTime);
                        resultMap.put("timeLong",timeLong+"ms");

                        log.info("File upload time ->  " + timeLong + "ms");
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if(out!=null){
                            out.close();
                        }
                    }

                } else {
                    resultMap.put("msg", "参数缺失");
                }
                showJson(resultMap, request, response);
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Map<String,String> getLocalConfigMap(){
        Map<String,String> map = new HashMap<String,String>();
        String localFileName = "config.properties";

        //从本地config文件读取配置
        String fileUploadDirectory =  ToolsUtil.getInstance().getLocalByKey(localFileName, "UPLOAD_DIRECTORY");//获取文件需要上传到的根目录
        String fileDownloadUrl =  ToolsUtil.getInstance().getLocalByKey(localFileName, "DOWNLOAD_URL");//获取文件需要下载的根目录地址
        String fileAllowType =  ToolsUtil.getInstance().getLocalByKey(localFileName, "ALLOW_TYPE");//允许上传的文件类型
        String fileMaxLength =  ToolsUtil.getInstance().getLocalByKey(localFileName, "FILE_MAX_LENGTH");//文件大小最大值
        if (StrUtil.isNull(fileUploadDirectory) || StrUtil.isNull(fileDownloadUrl)){
            log.error(localFileName+"配置文件缺少[UPLOAD_DIRECTORY、DOWNLOAD_URL、ALLOW_TYPE、FILE_MAX_LENGTH]等相关配置");
        }else{
            map.put("fileUploadDirectory",fileUploadDirectory);
            map.put("fileDownloadUrl",fileDownloadUrl);
            map.put("fileAllowType",fileAllowType);
            map.put("fileMaxLength",fileMaxLength);
        }
        return map;
    }

    private void showJson(Object obj, HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        if (!ToolsUtil.isEmpty(obj)) {
            try {
                JSONObject array = JSONObject.fromObject(obj);
                log.info("File upload finish return->  " + array.toString());
                log.info("File upload end！！");
                this.outputJson(array.toString(), response);
            } catch (IOException e) {
                log.info("File upload end！！");
                gotoErrorPage(request, response, "请求的Action地址未定义");
            }
        }

    }

    /**
     * description:  跳到到错误页面
     */
    protected void gotoErrorPage(HttpServletRequest request, HttpServletResponse response,
            String errors) throws IOException, ServletException {
        String header = request.getHeader("X-Requested-With");
        boolean isAjax = "XMLHttpRequest".equalsIgnoreCase(header) ? true : false;
        boolean isMobile = PortalPubFactory.getInstance().JudgeIsMoblie(request);
        if (isAjax || isMobile) {
            ResultMsg rmsg = new ResultMsg("F", StrUtil.doNull(errors, "请求的Action地址未定义"));
            JSONObject array = JSONObject.fromObject(rmsg);
            this.outputJson(array.toString(), response);
        } else {
            request.setAttribute("errors", "请求的Action地址未定义");
            request.getRequestDispatcher("/doSendPage?toPage=error").forward(request, response);
        }
    }

    private String getFileSize(long leng) {
        String re = String.valueOf(leng);
        if (leng >= (1024 * 1024 * 1024)) {
            re = NumberUtils.divide(leng, (1024 * 1024 * 1024), 0) + "G";
        } else if (leng >= (1024 * 1024)) {
            re = NumberUtils.divide(leng, (1024 * 1024), 0) + "M";
        } else if (leng >= (1024)) {
            re = NumberUtils.divide(leng, (1024), 0) + "KB";
        } else {
            re = leng + "";
        }
        return re;
    }

    //验证文件类型
    private boolean checkFileType(FileItem fileItem,String setting_fileTypes) {
        boolean isok = false;
        String fileContentType = fileItem.getContentType();
        log.info("File contentType->  " + fileContentType);
        Map<String,String> settingMap = getSettingTypesMap(setting_fileTypes);
        log.info("File settingMap->  " + settingMap);
        String this_file_type_0 = fileContentType.split("/")[0].toLowerCase();
        String this_file_type_1 = fileContentType.split("/")[1].toLowerCase();
        if(settingMap.containsKey(this_file_type_0)){
            String setting_fileType_values = settingMap.get(this_file_type_0);
            if(setting_fileType_values.indexOf("*")>-1){
                isok = true;
            }else{
                if(setting_fileType_values.indexOf(this_file_type_1)>-1){
                    isok = true;
                }else{
                    log.error("File contentType matching->  [" + fileContentType +"]不满足["+setting_fileType_values+"]内匹配规则！");
                    isok = false;
                }
            }
        }else{
            log.error("File contentType matching->  [" + fileContentType +"]不存在于["+setting_fileTypes+"]内！");
            isok = false;
        }
        return isok;
    }

    //转换配置文件所指定的文件类型
    private Map<String,String> getSettingTypesMap(String File_ALLOW_TYPE){
        String [] settingArrays = File_ALLOW_TYPE.split(",");
        Map<String,String> settingMap =new HashMap<String,String>();
        if(File_ALLOW_TYPE != null && File_ALLOW_TYPE.length() > 0){
            for (int i=0;i<settingArrays.length ; i++){
                String keys = settingArrays[i].split("/")[0].toLowerCase();
                String vals = "";
                if(settingMap.containsKey(keys)){
                    vals = settingMap.get(keys)+",";
                }
                vals+=settingArrays[i].split("/")[1].toLowerCase();
                settingMap.put(keys,vals);
            }
        }
        return settingMap;
    }
}

